package commons.reflect.examples;

import java.lang.reflect.*;
import java.util.Arrays;

public class GenericReflect {
    public static void printMethod(Method method) {
        System.out.print(Modifier.toString(method.getModifiers()));
        System.out.print(" ");
        printTypes(method.getTypeParameters(), "<", ", ", ">", true);
        printType(method.getGenericReturnType(), false);
        System.out.print(" " + method.getName() + "(");
        printTypes(method.getGenericParameterTypes(), "", ", ", "", false);
        System.out.println(")");
    }
    
    public static void printClass(Class<?> clazz) {
        System.out.print(clazz);
        printTypes(clazz.getTypeParameters(), "<", ",", ">", true);
        Type sc = clazz.getGenericSuperclass();
        if (sc != null) {
            System.out.print(" extends ");
            printType(sc, false);
        }
        printTypes(clazz.getGenericInterfaces(), " implements ", ",", "", false);
        System.out.println();
    }
    
    public static void printTypes(Type[] types, String prefix, String seperator, String suffix, boolean isDefination) {
        if (prefix.equals(" extends ") && Arrays.equals(types, new Type[] {Object.class})) {
            return;
        }
        if (types.length > 0) {
            System.out.print(prefix);
        }
        for (int i = 0; i < types.length; i++) {
            if (i > 0) System.out.print(seperator);
            printType(types[i], isDefination);
        }
        if (types.length > 0) {
            System.out.print(suffix);
        }
    }

    public static void printType(Type type, boolean isDefination) {
        if (type instanceof Class<?>) {
            var cl = (Class<?>) type;
            System.out.print(cl.getName());
        } else if (type instanceof TypeVariable<?>) {
            var t = (TypeVariable<?>) type;
            System.out.print(t.getName());
            if (isDefination) {
                printTypes(t.getBounds(), " extends ", "&", "", false);
            }
        } else if (type instanceof WildcardType) {
            var t = (WildcardType) type;
            System.out.print("?");
            printTypes(t.getUpperBounds(), " extends ", "&", "", false);
            printTypes(t.getLowerBounds(), " super ", "&", "", false);
        } else if (type instanceof ParameterizedType) {
            var t = (ParameterizedType) type;
            var owner = t.getOwnerType();
            if (owner != null) {
                printType(owner, false);
                System.out.print(".");
            }
            printType(t.getRawType(), false);
            printTypes(t.getActualTypeArguments(), "<", ",", ">", false);
        } else if (type instanceof GenericArrayType) {
            var t = (GenericArrayType) type;
            printType(t.getGenericComponentType(), isDefination);
            System.out.print("[]");
        }
    }
}
